/**
 * Copyright (c) 2017-2024 Nop Platform. All rights reserved.
 * Author: canonical_entropy@163.com
 * Blog:   https://www.zhihu.com/people/canonical-entropy
 * Gitee:  https://gitee.com/canonical-entropy/nop-entropy
 * Github: https://github.com/entropy-cloud/nop-entropy
 */
package com.gitee.wsl.struct.table.utils


import com.gitee.wsl.func.Function
import com.gitee.wsl.func.predicate.Predicate
import com.gitee.wsl.struct.table.ICell
import com.gitee.wsl.struct.table.ICellView
import com.gitee.wsl.struct.table.IRow
import com.gitee.wsl.struct.table.IRowView
import com.gitee.wsl.struct.table.ITable
import com.gitee.wsl.struct.table.ITableView
import com.gitee.wsl.struct.table.ProcessResult


object TableImpls {
    fun isAllColumnCellSatisfy(
        table: ITableView,
        colIndex: Int,
        predicate: Predicate<ICellView?>
    ): Boolean {
        var i = 0
        val n: Int = table.rowCount
        while (i < n) {
            val cell = table.getCell(i, colIndex)
            if (!predicate.test(cell)) return false
            i++
        }
        return true
    }

    fun isSomeColumnCellSatisfy(
        table: ITableView,
        colIndex: Int,
        predicate: Predicate<ICellView?>
    ): Boolean {
        var i = 0
        val n: Int = table.rowCount
        while (i < n) {
            val cell = table.getCell(i, colIndex)
            if (predicate.test(cell)) return true
            i++
        }
        return false
    }

    fun isAllRowCellSatisfy(
        table: ITableView,
        rowIndex: Int,
        predicate: Predicate<ICellView?>
    ): Boolean {
        val row = table.getRow(rowIndex) ?: return false
        return row.forEachCell<ICellView>(rowIndex) { cell, i, j ->
            if (predicate.test(cell))
                return@forEachCell ProcessResult.CONTINUE
            ProcessResult.STOP
        } == ProcessResult.CONTINUE
    }

    fun isSomeRowCellSatisfy(
        table: ITableView,
        rowIndex: Int,
        predicate: Predicate<ICellView?>
    ): Boolean {
        val row = table.getRow(rowIndex) ?: return false
        return row.forEachCell<ICellView>(rowIndex) { cell, _, _ ->
            if (predicate.test(cell))
                return@forEachCell ProcessResult.STOP
            ProcessResult.CONTINUE
        } == ProcessResult.STOP
    }

    fun <T> getMatrixValues(
        table: ITableView,
        fn: Function<ICellView?, T>
    ): List<List<T>> {
        val nRow: Int = table.rowCount
        val nCol: Int = table.colCount

        val ret: MutableList<List<T>> = ArrayList<List<T>>(nRow)

        for (row in table.rows) {
            val values: MutableList<T> = ArrayList<T>(nCol)

            if (row == null) {
                for (j in 0 until nCol) {
                    values.add(fn.apply(null))
                }
            } else {
                for (j in 0 until nCol) {
                    val cell = row.getCell(j)
                    val value: T = fn.apply(cell)
                    values.add(value)
                }
            }
            ret.add(values)
        }
        return ret
    }

    fun <T> getRowValues(
        table: ITableView, headers: List<String?>,
        fn: Function<ICellView?, T>
    ): List<Map<String, T>> {
        val nRow: Int = table.rowCount
        val ret: MutableList<Map<String, T>> = ArrayList<Map<String, T>>(nRow)
        var rowIndex = 0
        for (row in table.rows) {
            val map: MutableMap<String, T> = HashMap<String, T>(headers.size)
            if (row == null) {
                var i = 0
                val n = headers.size
                while (i < n) {
                    val header = headers[i]
                    if (header == null) {
                        i++
                        continue
                    }
                    map[header] = fn.apply(null)
                    i++
                }
            } else {
                row.forEachCell<ICellView>(rowIndex) { cell, i, j ->
                    val header = headers[j] ?: return@forEachCell ProcessResult.CONTINUE
                    map[header] = fn.apply(cell)
                    ProcessResult.CONTINUE
                }
            }
            rowIndex++
            ret.add(map)
        }
        return ret
    }

    fun <T> getRowCellValues(
        row: IRowView,
        fn: Function<ICellView?, T>
    ): List<T> {
        val nCol: Int = row.colCount
        val ret: MutableList<T> = ArrayList<T>(nCol)

        row.forEachCell<ICellView>(0) { cell, i, j ->
            ret.add(fn.apply(cell))
            ProcessResult.CONTINUE
        }

        return ret
    }

    fun <T : IRow> setCells(
        table: ITable<T>, rowIndex: Int, colIndex: Int, view: ITableView,
        transformer: Function<ICellView, ICell>?
    ) {
        var i = 0
        val n: Int = view.rowCount
        while (i < n) {
            val row = view.getRow(i)!!
            var j = 0
            val m: Int = row.colCount
            while (j < m) {
                val icell = row.getCell(j)
                if (icell == null) {
                    table.setCell(rowIndex + i, colIndex + j, null)
                    j++
                    continue
                }
                if (icell.isProxyCell) {
                    j++
                    continue
                }

                val cellView = icell.realCell!!
                val cell = transformer?.apply(cellView) ?: cellView.cloneInstance() as ICell
                table.setCell(rowIndex + i, colIndex + j, cell)
                j++
            }
            i++
        }
    }
}